﻿using NumSharp;
using OpenCvSharp;
using ScottPlot;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;

namespace NIDViewer
{
    public class ImageGenerator
    {
        public static (byte[] flattenedImage, int width, int height) GenerateShowPoints(
        List<double> originPoint,
        int xres,
        int yres,
        double xreal,
        double yreal,
        string generateShowImage = "physical")
        {
            // 转换原始数据为纳米单位
            var originData = np.array(originPoint.ToArray()) * 1e9;
            var resizeArray = originData.reshape(yres, xres);

            // 计算直方图
            var flattenedData = originData.flatten();
            var (hist, binEdges) = CalculateHistogram(flattenedData.GetData<double>().ToArray(), 100);
            var binCenters = new double[binEdges.Length - 1];
            for (int i = 0; i < binCenters.Length; i++)
            {
                binCenters[i] = (binEdges[i] + binEdges[i + 1]) / 2;
            }

            // 寻找峰值
            var peaks = FindPeaks(hist, Math.Max(yres * xres / 33.0, 20));

            double minRange = resizeArray.min();
            double maxRange = Percentile(resizeArray, 95);

            try
            {
                if (peaks.Count == 0)
                {
                    minRange = Percentile(resizeArray, 2);
                    maxRange = Percentile(resizeArray, 95);
                }
                else
                {
                    int peaksLast = peaks[peaks.Count - 1];
                    maxRange = binCenters[99];
                    for (int i = peaksLast; i < 100; i++)
                    {
                        if (hist[i] <= hist[peaksLast] / 50.0)
                        {
                            maxRange = binCenters[i];
                            break;
                        }
                    }
                    minRange = resizeArray.min();
                }
            }
            catch (Exception ex)
            {
                // 保持默认值
            }

            // 裁剪图像
            var grayImage = Clip(resizeArray, minRange, maxRange);
            var flatten = grayImage;

            // 两种显示方式
            double wh = xreal / yreal;
            if ((wh > 10.0 || 1 / wh > 10.0 || wh < 1) && generateShowImage == "physical")
            {
                generateShowImage = "pixel";
            }

            NDArray arrayList;
            if (generateShowImage == "pixel")
            {
                arrayList = flatten;
            }
            else
            {
                var clipArray = Clip(resizeArray, minRange, maxRange);
                var newShape = CalculateMinDimensions(xres, yres, wh);
                int newHeight = (int)newShape[0];
                int newWidth = (int)newShape[1];
                arrayList = RepeatArrayToNewSize(clipArray, newWidth, newHeight);
            }

            // 生成图像
            var expandArr = arrayList;
            int plotHeight = expandArr.shape[0];
            int plotWidth = expandArr.shape[1];

            Mat image;
            if (plotWidth == 1 || plotHeight == 1)
            {
                // 绘制线图
                image = PlotLineChart(expandArr.flatten().GetData<double>().ToArray().ToArray(), 600, 300);
            }
            else
            {
                // 绘制热图
                int figWidth = Math.Max(100, plotWidth);
                int figHeight = Math.Max(100, plotHeight);
                image = PlotHeatmap(expandArr, figWidth, figHeight);
            }

            // 转换为BGR（如果是RGBA）
            if (image.Channels() == 4)
            {
                Cv2.CvtColor(image, image, ColorConversionCodes.RGBA2BGR);
            }

            // 调整大小
            if (image.Height > 600 || image.Width > 600)
            {
                double scale = 600.0 / Math.Max(image.Height, image.Width);
                int newHeight = (int)(image.Height * scale);
                if (newHeight == 0) newHeight = image.Height;
                int newWidth = (int)(image.Width * scale);
                if (newWidth == 0) newWidth = image.Width;

                Cv2.Resize(image, image, new OpenCvSharp.Size(newWidth, newHeight), 0, 0, InterpolationFlags.Area);
            }

            // 转换为字节数组
            byte[] flattenedImage = new byte[image.Total() * image.Channels()];
            System.Runtime.InteropServices.Marshal.Copy(image.Data, flattenedImage, 0, flattenedImage.Length);

            return (flattenedImage, image.Width, image.Height);
        }

        private static (double[] hist, double[] binEdges) CalculateHistogram(double[] data, int bins)
        {
            double min = data.Min();
            double max = data.Max();
            double binWidth = (max - min) / bins;

            double[] hist = new double[bins];
            double[] binEdges = new double[bins + 1];

            for (int i = 0; i <= bins; i++)
            {
                binEdges[i] = min + i * binWidth;
            }

            foreach (var value in data)
            {
                int binIndex = (int)((value - min) / binWidth);
                if (binIndex >= bins) binIndex = bins - 1;
                if (binIndex < 0) binIndex = 0;
                hist[binIndex]++;
            }

            return (hist, binEdges);
        }

        private static List<int> FindPeaks(double[] data, double heightThreshold)
        {
            List<int> peaks = new List<int>();

            for (int i = 1; i < data.Length - 1; i++)
            {
                if (data[i] > data[i - 1] && data[i] > data[i + 1] && data[i] >= heightThreshold)
                {
                    peaks.Add(i);
                }
            }

            return peaks;
        }

        private static double Percentile(NDArray array, double percentile)
        {
            var data = array.flatten().GetData<double>().OrderBy(x => x).ToArray();
            int index = (int)(data.Length * percentile / 100.0);
            if (index >= data.Length) index = data.Length - 1;
            return data[index];
        }

        private static NDArray Clip(NDArray array, double min, double max)
        {
            var data = array.GetData<double>();
            var clipped = data.Select(x => Math.Max(min, Math.Min(max, x))).ToArray();
            return np.array(clipped).reshape(array.shape);
        }

        private static int[] CalculateMinDimensions(int xres, int yres, double aspectRatio)
        {
            // 简化实现 - 根据宽高比计算新尺寸
            int newHeight = yres;
            int newWidth = (int)(yres * aspectRatio);
            return new int[] { newHeight, newWidth };
        }

        private static NDArray RepeatArrayToNewSize(NDArray array, int newWidth, int newHeight)
        {
            int oldHeight = array.shape[0];
            int oldWidth = array.shape[1];

            var result = np.zeros(newHeight, newWidth);

            for (int i = 0; i < newHeight; i++)
            {
                for (int j = 0; j < newWidth; j++)
                {
                    int oldI = i * oldHeight / newHeight;
                    int oldJ = j * oldWidth / newWidth;
                    result[i, j] = array[oldI, oldJ];
                }
            }

            return result;
        }

        private static Mat PlotLineChart(double[] data, int width, int height)
        {
            var plt = new ScottPlot.Plot();
            plt.Add.Signal(data);
            plt.HideGrid();
            plt.Layout.Frameless();

            // 保存为图像
            var image = plt.GetImage(width, height);

            // 转换为OpenCV Mat
            using (var ms = new System.IO.MemoryStream())
            {
                var savePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{DateTime.Now:yyyyMMddHHmmdd}LineChart.png");
                image.Save(savePath, ImageFormat.Png);
                byte[] imageBytes = File.ReadAllBytes(savePath);
                return Cv2.ImDecode(imageBytes, ImreadModes.Color);
            }
        }

        private static Mat PlotHeatmap(NDArray data, int width, int height)
        {
            int rows = data.shape[0];
            int cols = data.shape[1];

            double[,] heatmapData = new double[rows, cols];
            var flatData = data.GetData<double>();

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    heatmapData[i, j] = flatData[i * cols + j];
                }
            }

            var plt = new ScottPlot.Plot();
            var hm = plt.Add.Heatmap(heatmapData);
            hm.Colormap = new ScottPlot.Colormaps.Viridis(); // 使用Viridis色图
            plt.HideGrid();
            plt.Layout.Frameless();

            // 保存为图像
            var image = plt.GetImage(width, height);

            // 转换为OpenCV Mat
            using (var ms = new System.IO.MemoryStream())
            {
                var savePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{DateTime.Now:yyyyMMddHHmmdd}Heatmap.png");
                image.Save(savePath, ImageFormat.Png);
                byte[] imageBytes = File.ReadAllBytes(savePath);
                return Cv2.ImDecode(imageBytes, ImreadModes.Color);
            }
        }
    }
}
